﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Xml.Serialization;

namespace Orz.Framework.Foundation.Serialization
{
    /// <summary>
    /// Object serializer.
    /// </summary>
    public class ObjectSerializer
    {
        /// <summary>
        /// The cache of xml serializer.
        /// </summary>
        static Dictionary<Type, XmlSerializer> xmlSerializerCache = new Dictionary<Type, XmlSerializer>();

        /// <summary>
        /// Serializes to XML.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <param name="extraTypes">The extra types.</param>
        /// <returns></returns>
        public static string SerializeToXml(object obj, params Type[] extraTypes)
        {
            try
            {
                if (extraTypes == null)
                    extraTypes = new Type[] { };
                XmlSerializer xmlSerializer = GetXmlSerializer(obj.GetType(), extraTypes);

                using (StringWriter sw = new StringWriter())
                {
                    xmlSerializer.Serialize(sw, obj);
                    return sw.ToString();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Gets the serializer.
        /// </summary>
        /// <param name="objType">Type of the obj.</param>
        /// <param name="extraTypes">The extra types.</param>
        /// <returns></returns>
        private static XmlSerializer GetXmlSerializer(Type objType, Type[] extraTypes)
        {
            if (!xmlSerializerCache.ContainsKey(objType))
            {
                XmlSerializerFactory factory = new XmlSerializerFactory();
                XmlSerializer xmlSerializer = factory.CreateSerializer(objType, extraTypes);
                xmlSerializerCache.Add(objType, xmlSerializer);
            }
            return xmlSerializerCache[objType];
        }

        /// <summary>
        /// Serializes the specified obj.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <param name="targetFileName">Name of the target file.</param>
        /// <param name="extraTypes">The extra types.</param>
        /// <returns></returns>
        public static bool SerializeToXml(object obj, string targetFileName, params Type[] extraTypes)
        {
            try
            {
                string xml = SerializeToXml(obj, extraTypes);
                using (TextWriter textWriter = new StreamWriter(targetFileName))
                {
                    textWriter.Write(xml);
                }
                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Deserializes the specified file name.
        /// </summary>
        /// <typeparam name="T">The object type</typeparam>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="extraTypes">The extra types.</param>
        /// <returns></returns>
        public static T DeserializeFromXmlFile<T>(string fileName, params Type[] extraTypes)
        {
            try
            {
                if (extraTypes == null)
                    extraTypes = new Type[] { };
                System.Xml.Serialization.XmlSerializer xmlSerializer = GetXmlSerializer(typeof(T), extraTypes);
                using (TextReader textReader = new StreamReader(fileName))
                {
                    return (T)xmlSerializer.Deserialize(textReader);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Deserializes the xml content string, added by dave 20091209
        /// </summary>
        /// <typeparam name="T">The object type</typeparam>
        /// <param name="xmlStr">xml content</param>
        /// <param name="extraTypes">The extra types.</param>
        /// <returns></returns>
        public static T DeserializeFromXml<T>(string xmlStr, params Type[] extraTypes)
        {
            try
            {
                if (extraTypes == null)
                    extraTypes = new Type[] { };
                System.Xml.Serialization.XmlSerializer xmlSerializer = GetXmlSerializer(typeof(T), extraTypes);

                using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(xmlStr)))
                {
                    T t = (T)xmlSerializer.Deserialize(stream);
                    return t;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Serializes to bytes.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <returns></returns>
        public static byte[] SerializeToBytes(object obj)
        {
            BinaryFormatter binFormatter = new BinaryFormatter();
            using (MemoryStream memStream = new MemoryStream())
            {
                binFormatter.Serialize(memStream, obj);
                return memStream.ToArray();
            }
        }

        /// <summary>
        /// Deserializes from bytes.
        /// </summary>
        /// <param name="buffer">The buffer.</param>
        /// <returns></returns>
        public static object DeserializeFromBytes(byte[] buffer)
        {
            using (MemoryStream memStream = new MemoryStream(buffer))
            {
                BinaryFormatter binFormatter = new BinaryFormatter();
                return binFormatter.Deserialize(memStream);
            }
        }
    }
}
